package utilidades;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import Inscripcion.Participante;
import Inscripcion.Usuario;

/**
 * Escritor es la clase através de la que se interactúa con las estructuras de datos que forman la lógica
 * de la aplicación. En las consultoras proporciona copias de las mismas por mayor seguridad, proporciona además
 * métodos sincronizados para impedir escrituras concurrentes en las mismas.
 * 
 * @author Zhakarias
 *
 */
public class Escritor {

	
	/**
	 * Este método devuelve la estructura de datos que contiene todos los participantes inscritos en la actividad
	 * recibe como parámetro el contexto del conjunto de servlets ya que no puede acceder a el por si solo al no 
	 * tratarse de uno de los servlets de la aplicacián
	 * 
	 * @param servletContext
	 * @return
	 */
	public static HashMap<String, Participante> getParticipantes(ServletContext servletContext){
		// TODO Auto-generated method stub
		HashMap<String, Participante> participantes = 
				(HashMap<String, Participante>) servletContext.getAttribute("participantes");
		
		HashMap<String, Participante> dev = new HashMap<String, Participante>();
		
		Iterator<String> it = participantes.keySet().iterator();
		
		while(it.hasNext()){
			Participante aux = participantes.get(it.next());
			Participante copia = (Participante) aux.clone();
			
			dev.put(copia.getUserID(),copia);
		}
		
		return dev;
	}
	
	/**
	 * Método seguro para realizar las escrituras en la estructura de datos de los participantes fuerza a añadirlos
	 * de uno en uno
	 * 
	 * @param servletContext contexto de la aplicación
	 * @param p nuevo participante a añadir
	 */
	public static synchronized void addParticipante(ServletContext servletContext, Participante p) {
		
		HashMap<String, Participante> participantes = 
				(HashMap<String, Participante>) servletContext.getAttribute("participantes");
		
		participantes.put(p.getUserID(), p);
		
		// Guardamos en el contexto
		servletContext.setAttribute("participantes", participantes);
		
		// Guardamos en el archivo de persistencia
		String rutaRelativa = "/WEB-INF/Persistencia/usuarios.xml";
		String rutaDisco = servletContext.getRealPath(rutaRelativa);
		
		Escritor.guardarParticipantes(rutaDisco, participantes);
	}
	
	/**
	 * Método que extrae la estructura de participantes del contexto por seguridad se clonan los objetos
	 * para impedir modificacones fuera de la sincronización que gestionamos en esta clase
	 * 
	 * @param servletContext
	 * @return usuarios registrados en la aplicación
	 */
	public static HashMap<String, Usuario> getRegistrados(ServletContext servletContext){
		// TODO Auto-generated method stub
		HashMap<String, Usuario> registrados = 
				(HashMap<String, Usuario>) servletContext.getAttribute("registrados");
		
		HashMap<String, Usuario> dev = new HashMap<String, Usuario>();
		
		Iterator<String> it = registrados.keySet().iterator();
		
		while(it.hasNext()){
			Usuario aux = registrados.get(it.next());
			Usuario copia = (Usuario) aux.clone();
			
			dev.put(copia.getUsername(),copia);
		}
		
		return dev;
	}

	
	public static synchronized void addRegistrado(ServletContext servletContext, Usuario u) {
		
		HashMap<String, Usuario> registrados = 
				(HashMap<String, Usuario>) servletContext.getAttribute("registrados");
		
		registrados.put(u.getUsername(), u);
		
		servletContext.setAttribute("registrados", registrados);
	}
	
	
	
	public static synchronized boolean eliminarParticipante(ServletContext servletContext, Participante p){
		boolean eliminado = false;
		HashMap<String, Participante> participantes = 
				(HashMap<String, Participante>) servletContext.getAttribute("participantes");
		if (p != null){
			participantes.remove(p.getUserID());
			eliminado = true;
		}
		servletContext.setAttribute("participantes", participantes);
		
		return eliminado;
	}
	
	public static Participante buscarParticipante(ServletContext servletContext, String username){
		Participante p = null;
		HashMap<String, Participante> participantes = 
				(HashMap<String, Participante>) servletContext.getAttribute("participantes");
		
		p = participantes.get(username);
		
		
		return p;
		
	}
	
	public static boolean modificarParticipante(ServletContext servletContext, Participante viejo, Participante nuevo){
		
		boolean modificado = false;
		HashMap<String, Participante> participantes = 
				(HashMap<String, Participante>) servletContext.getAttribute("participantes");
		
		if (viejo != null){
			participantes.get(viejo.getUserID()).setApellido1(nuevo.getApellido1());
			participantes.get(viejo.getUserID()).setApellido2(nuevo.getApellido2());
			participantes.get(viejo.getUserID()).setNombre(nuevo.getNombre());
			participantes.get(viejo.getUserID()).setFecha(nuevo.getFecha());
			participantes.get(viejo.getUserID()).setSexo(nuevo.getSexo());
			modificado = true;
		}
		
		servletContext.setAttribute("participantes", participantes);
	
		return modificado;
	}
	
	
	public static Usuario buscarUsuario(ServletContext servletContext, String username){
		Usuario u = null;
		HashMap<String, Usuario> registrados = 
				(HashMap<String, Usuario>) servletContext.getAttribute("registrados");
		
		Iterator<String> it = registrados.keySet().iterator();
		
		while(it.hasNext()){
			Usuario aux = registrados.get(it.next());
			String s = aux.getUsername();
			//String s = registrados.get(aux.getId()).getUsername(); 
		
			if (s.equals(username)){			
				System.out.println("se ha metido");
				u = aux;
			}
		}		
	
		return u;
		
	}
	
	public static boolean eliminarUsuario(ServletContext servletContext, Usuario u){
		boolean eliminado = false;
		HashMap<String, Usuario> registrados = 
				(HashMap<String, Usuario>) servletContext.getAttribute("registrados");
		if (u != null){
			//registrados.remove(u.getId());
			registrados.remove(u.getUsername());
			eliminado = true;
		}
		servletContext.setAttribute("registrados", registrados);
		
		return eliminado;
	}

	
	public static boolean modificarUsuario(ServletContext servletContext, Usuario viejo, Usuario nuevo){
		
		boolean modificado = false;
		HashMap<String, Usuario> registrados = 
				(HashMap<String, Usuario>) servletContext.getAttribute("registrados");
		
		if (viejo != null){
			registrados.get(viejo.getUsername()).setNombre(nuevo.getNombre());
			registrados.get(viejo.getUsername()).setApellido1(nuevo.getApellido1());
			registrados.get(viejo.getUsername()).setApellido2(nuevo.getApellido2());
			registrados.get(viejo.getUsername()).setCorreo(nuevo.getCorreo());
			registrados.get(viejo.getUsername()).setSexo(nuevo.getSexo());
			registrados.get(viejo.getUsername()).setPassword(nuevo.getPassword());
			
			modificado = true;
		}
		
		//Guardamos en el contexto
		servletContext.setAttribute("registrados", registrados);
		
		//Guardamos en el archivo de persistencia
		// Obtener la ruta del fichero de persistencia de participantes
				String rutaRelativa = "/WEB-INF/Persistencia/registrados.xml";
				String rutaDisco = servletContext.getRealPath(rutaRelativa);
		
		return modificado;
	}
	
	public static void guardarParticipantes(String rutaDisco,
			HashMap<String, Participante> participantes) {
		
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			// Creo el elemento raiz de la estructura
			Document doc = docBuilder.newDocument();
			Element raiz = doc.createElement("ListaUsuarios");
			// Añado el elemento al documento
			doc.appendChild(raiz);
			
			// Obtengo todos los participantes de la tabla
			ArrayList<Participante> listaParticipantes = getListaParticipantes(participantes);
			
			// Itero sobre ellos y los voy añadiendo a la raiz
			for(int i = 0; i < listaParticipantes.size();i++){
				Element nodo = getElementoParticipante(listaParticipantes.get(i),doc);
				raiz.appendChild(nodo);
			}
			// Genero el fichero de salida
			
			//TODO Pasar la salida a metodo independiente para reciclar el c�digo
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer;
			try {
				transformer = transformerFactory.newTransformer();
			
				DOMSource source = new DOMSource(doc);
				StreamResult result = new StreamResult(new File(rutaDisco));
	 
				// Salida por consola para testeo
				//result = new StreamResult(System.out);
	 
			transformer.transform(source, result);
			
			} catch (TransformerConfigurationException ce) {
				// TODO Auto-generated catch block
				ce.printStackTrace();
			} catch (TransformerException te) {
				// TODO Auto-generated catch block
				te.printStackTrace();
			}
			System.out.println("Fichero salvado");
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		
	}
	
	/**
	 * Método que crea la etiqueta xml de participante apartir de una instancia de la clase Participante
	 * 
	 * @param participante
	 * @param doc
	 * @return
	 */
	
	private static Element getElementoParticipante(Participante participante, Document doc) {
		
		//Creamos la raiz del usuario
		Element dev = doc.createElement("Usuario");
		Attr attr = doc.createAttribute("uid");
		attr.setValue(participante.getUserID());
		dev.setAttributeNode(attr);
		
		//Introducimos los distintos elementos hijos de usuario
		Element nombre = doc.createElement("Nombre");
		nombre.appendChild(doc.createTextNode(participante.getNombre()));
		dev.appendChild(nombre);
		
		Element apellido1 = doc.createElement("Apellido1");
		apellido1.appendChild(doc.createTextNode(participante.getApellido1()));
		dev.appendChild(apellido1);
		
		Element apellido2 = doc.createElement("Apellido2");
		apellido2.appendChild(doc.createTextNode(participante.getApellido2()));
		dev.appendChild(apellido2);
		
		Element fecha = doc.createElement("Fecha");
		fecha.appendChild(doc.createTextNode(participante.getFecha()));
		dev.appendChild(fecha);
		
		Element sexo = doc.createElement("Sexo");
		sexo.appendChild(doc.createTextNode(participante.getSexo()));
		dev.appendChild(sexo);
		
		return dev;
	}
	
	/**
	 * Extrae el array con los participantes en la actividad 
	 * 
	 * @param participantes
	 * @return
	 */
	private static ArrayList<Participante> getListaParticipantes(
			HashMap<String, Participante> participantes) {

		ArrayList<Participante> dev = new ArrayList<Participante>();
		Iterator<String> it = participantes.keySet().iterator();
		
		while(it.hasNext()){
			Participante aux = participantes.get(it.next());
			if(aux != null)
				dev.add(aux);
		}
		
		return dev;
	}
	
	/**
	 * Este método guarda en el fichero de persistencia usuarios.xml es invocado tanto al realizar
	 * una adición de usuario como al salir del servidor por parte del Listener MyServletListenerRegistro
	 * en el método contextDestroyed.
	 * 
	 * @param rutaDisco
	 * @param registrados
	 */
	public static void guardarRegistrados(String rutaDisco,
			HashMap<String, Usuario> registrados) {
		
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		try {
			
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			// Creo el elemento raiz de la estructura
			Document doc = docBuilder.newDocument();
			Element raiz = doc.createElement("ListaRegistrados");
			// Añado el elemento al documento
			doc.appendChild(raiz);
			
			// Obtengo todos los participantes de la tabla
			ArrayList<Usuario> listaRegistrados = getListaRegistrados(registrados);
			
			// Itero sobre ellos y los voy añadiendo a la raiz
			for(int i = 0; i < listaRegistrados.size();i++){
				Element nodo = getElementoRegistrado(listaRegistrados.get(i),doc);
				raiz.appendChild(nodo);
			}
			// Genero el fichero de salida
			
			//TODO Pasar la salida a metodo independiente para reciclar el c�digo
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer;
			try {
				transformer = transformerFactory.newTransformer();
			
				DOMSource source = new DOMSource(doc);
				StreamResult result = new StreamResult(new File(rutaDisco));
	 
				// Output to console for testing
				//result = new StreamResult(System.out);
	 
			transformer.transform(source, result);
			
			} catch (TransformerConfigurationException ce) {
				// TODO Auto-generated catch block
				ce.printStackTrace();
			} catch (TransformerException te) {
				// TODO Auto-generated catch block
				te.printStackTrace();
			}
			System.out.println("Fichero salvado");
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private static Element getElementoRegistrado(Usuario registrado, Document doc) {
		
		//Creamos la raiz del usuario
		Element dev = doc.createElement("Usuario");
		Attr attr = doc.createAttribute("id");
		
		
	    
		attr.setValue(registrado.getId());
		dev.setAttributeNode(attr);
		
		//Introducimos los distintos elementos hijos de usuario
		Element nombre = doc.createElement("Nombre");
		nombre.appendChild(doc.createTextNode(registrado.getNombre()));
		dev.appendChild(nombre);
		
		Element apellido1 = doc.createElement("Apellido1");
		apellido1.appendChild(doc.createTextNode(registrado.getApellido1()));
		dev.appendChild(apellido1);
		
		Element apellido2 = doc.createElement("Apellido2");
		apellido2.appendChild(doc.createTextNode(registrado.getApellido2()));
		dev.appendChild(apellido2);
		
		Element correo = doc.createElement("Correo");
		correo.appendChild(doc.createTextNode(registrado.getCorreo()));
		dev.appendChild(correo);
		
		Element username = doc.createElement("Username");
		username.appendChild(doc.createTextNode(registrado.getUsername()));
		dev.appendChild(username);
		
		Element password = doc.createElement("Password");
		password.appendChild(doc.createTextNode(registrado.getPassword()));
		dev.appendChild(password);		
		
		
		Element sexo = doc.createElement("Sexo");
		sexo.appendChild(doc.createTextNode(registrado.getSexo()));
		dev.appendChild(sexo);
		
		return dev;
	}
	
	private static ArrayList<Usuario> getListaRegistrados(
			HashMap<String, Usuario> registrados) {

		ArrayList<Usuario> dev = new ArrayList<Usuario>();
		Iterator<String> it = registrados.keySet().iterator();
		
		while(it.hasNext()){
			Usuario aux = registrados.get(it.next());
			if(aux != null)
				dev.add(aux);
		}
		
		return dev;
	}
}
